<?php
declare(strict_types=1);
/**
 * 申通API中心
 */
namespace Service\VenderClient\WayBillExpressCompanyApi;
use Service\Code\Normal;
use Service\BaseService;
use Service\ExtraService\TaskWorker;
use Service\HttpServer\Http;
use Service\ExtraService\Guid;
class YtoCompanyApiClient extends BaseService
{
    
    //加密和解密接口
    const API_ENCRYPT_URL = 'http://customs.yto.net.cn/api/aes/encrypt';
    const API_DECRYPT_URL = 'http://customs.yto.net.cn/api/aes/decrypt';
    
    protected $apiDeclareAddressUrl='';
    protected $apiGetMailInfoUrl='';
    
    //客户端 ID，对接时圆通提供
    protected $clientKey    = '';
    protected $clientSecret = '';

    //key
    protected $key = '';
    //申报地区代码
    protected $channelCode = '';
    //PartnerID
    protected $partnerID = '';
    //CustomerCode
    protected $customerCode = '';
    
    protected $config;

    protected $businessTypeEnum = '';

    protected $messageTypeEnum = '';

    protected $productCode = 'PK';
    public function __construct( array $config = [] )
    {
        $this->config       = $config;
        $this->clientKey    = $config['ClientKey']; //;客户Key
        $this->clientSecret = $config['ClientSecret']; //加密Key

        $this->key          = $config['PartnerID']; //商家秘钥
        $this->partnerID    = $config['PartnerID'];
        $this->customerCode = $config['CustomerCode']; //客户Code
        
        $this->apiDeclareAddressUrl=$config['DeclareAddressUrl'];//申报接口地址
        $this->apiGetMailInfoUrl=$config['GetMailInfoUrl'];//取号接口地址


        $this->channelCode = $config['ChannelCode']; // 圆通内部自定义，口岸固定值

        $this->businessTypeEnum = $config['businessTypeEnum'] ?? '';

        $this->messageTypeEnum = $config['messageTypeEnum'] ?? '';
        $this->productCode = $config['productCode'] ?? 'PK';
    }
    
    public function logisticDeclare(array $declareInfo = []) {
        $receiveInfo = $declareInfo['receiveInfo'];
        $senderInfo  = $declareInfo['senderInfo'];
        $orderGoods  = $declareInfo['orderGoods'];
        $order       = $declareInfo['order'];
        //$isNeedGetMailNoFirst=empty($order['express_num'])?false:true;
        $ConsigneeAddress=$receiveInfo['ReceiverProvince'].$receiveInfo['ReceiverCity'].$receiveInfo['ReceiverCountry'].$receiveInfo['ReceiverAddress'];
        if (empty($order['express_num'])) {
            $mailInfo = $this->getNewMallInfo($declareInfo);            
            if ($mailInfo['code'] != Normal::SUC) {
                return $mailInfo;
            }
            $logisticsNo = $mailInfo['data']['waybillNo'];
        } else {
            $logisticsNo = $order['express_num']; //物流企业运单号
        }
        $orderSn   = $order['sn']; //i订单号
        
        $order_data = [
            'appType'            => 1, //企业报送类型: 1-新增 2-变更 3- 删除。默认为1
            'logisticsCode'      => $this->config['logisticCustomCode'], //海关备案的物流企业代码(福清、平潭、马尾)
            'logisticsName'      => $this->config['logisticCustomName'], //海关备案的物流企业名称(福清、平潭、马尾)
            'logisticsNo'        => $logisticsNo,
            'billNo'             => '',
            'orderNo'            => $orderSn,
            'freight'            => $order['price_express']??0, //运费: 国内派送的费用， 最多 3 位小数，无则写 0
            'insuredFee'         => $order['InsuredFee']??0, //保价费: 针对一个运单里的所有商品的保价金额，最多 3 位小数，无则写 0
            'currency'           => '142', //市制: 参考检验检疫给出的货币编码数据字典，中国: 142
            'weight'             => 0, //毛重: 整个包裹的重量，最多 3 位小数，无则写 0
            'packNo'             => 1, //件数: 一个运里包爸的包果数数，一股是1个
            'goodsInfo'          => '保税商品',//$logisticsGeneralInfo['GoodsInfo'], //要货物信息: 包中商品整体的描达，一个包中有多个 不同商品，可以用单个商品名称替代或者整体描达
            'consignee'          => $receiveInfo['ReceiverName'], //收货人名称
            'consigneeAddress'   => $ConsigneeAddress, //手货人地址
            'consigneeTelephone' => $receiveInfo['ReceiverPhone'], //收货人手机号
            'note'               => '', //备注信息 默认未空
        ];
        
        //获取圆通的加签接口数据
        $encryptData = [
            'aesKey' => $this->clientSecret,
            'msg'    => json_encode($order_data),
        ];
        $aesEncrypt = $this->execute('YuanTongMailEncrypt', self::API_ENCRYPT_URL, $encryptData);
        if (empty($aesEncrypt['data'])) {
            $message = $aesEncrypt['message'] ?? '获取失败圆通加签失败';
            return Normal::returnCode(Normal::FAIL,$message, $aesEncrypt);
        }
        $data = [
            'clientKey'   => $this->clientKey,
            'channelCode' => $this->channelCode,
            'data'        => $aesEncrypt['data'],
            'nonce'       => Guid::getGuidOnlyValue(),
            'timestamp'   => (float) ($this->getMillisecond()),
        ];
        if($this->businessTypeEnum)
        {
            $data['businessTypeEnum'] = $this->businessTypeEnum;
        }
        if($this->messageTypeEnum)
        {
            $data['messageTypeEnum'] = $this->messageTypeEnum;
        }
        $data['signature'] = $this->getSign($data);
        $result            = $this->execute('YuanTongMailDeclare', $this->apiDeclareAddressUrl, $data);
        $responseData=[
                'waybillNo'          => $mailInfo['data']['waybillNo']??'',//运单号
                'bigWord'            => $mailInfo['data']['bigWord']??'',//大字/三段码
                'packagePlace'       => $mailInfo['data']['packagePlace']??'',//集包地
                'originData'         => $mailInfo['data']['originData']??'',//返回的原始数据result字符串,
                'logisticCustomCode' => $mailInfo['data']['logisticCustomCode'] ?? '',//物流公司海关编码
                'logisticCustomName' => $mailInfo['data']['logisticCustomName'] ?? '',//物流公司海关名称
            ];
        if (isset($result['status']) && $result['status'] == Normal::SUC) {
            //self::addLocalStorageFileRecordByPath($file_path, $file, json_encode($result));
            return Normal::returnCode(Normal::SUC, "申报运单号成功", $responseData);
        }
        $message = $result['message'] ?? '申报运单号失败';
        return Normal::returnCode(Normal::FAIL, $message,$responseData);
    }
    
    /**
     * 获取物流公司面单号 新版本
     * @param array $logisticsGeneralInfo
     */
    public function getNewMallInfo(array $logisticsGeneralInfo = []) {
        $receiveInfo = $logisticsGeneralInfo['receiveInfo'];
        $senderInfo  = $logisticsGeneralInfo['senderInfo'];
        $orderGoods  = $logisticsGeneralInfo['orderGoods'];
        $order       = $logisticsGeneralInfo['order'];
        $orderData = [
            "logisticsNo"           => $order['sn'],
            "senderName"            => $senderInfo['SenderName'],
            "senderProvinceName"    => $senderInfo['SenderProvince'],
            "senderCityName"        => $senderInfo['SenderCity'],
            "senderCountyName"      => $senderInfo['SenderCountry'],
            "senderAddress"         => $senderInfo['SenderAddress'],
            "senderMobile"          => $senderInfo['SenderPhone'],
            "recipientName"         => $receiveInfo['ReceiverName'],
            "recipientProvinceName" => $receiveInfo['ReceiverProvince'],
            "recipientCityName"     => $receiveInfo['ReceiverCity'],
            "recipientCountyName"   => $receiveInfo['ReceiverCountry'],
            "recipientAddress"      => $receiveInfo['ReceiverAddress'],
            "recipientMobile"       => $receiveInfo['ReceiverPhone'],
            'productCode'           => $this->productCode,
//            "remark"                => $logisticsGeneralInfo['Note'],
//            'increments'            => [
//                [
//                    'type' => 4,
//                ],
//            ],
//            'goods'                 => [
//                [
//                    "name" => $logisticsGeneralInfo['GoodsInfo'],
//                ],
//            ],
            //'cstOrderNo'            => $logisticsGeneralInfo['TradeId'],
        ];
        $data        = json_encode($orderData, JSON_UNESCAPED_UNICODE);       
        $requestData = [
            'timestamp' => $this->getMillisecond(),
            'param'     => $data,
            'sign'      => base64_encode(md5($data . 'privacy_create_adapterv1' . $this->partnerID, true)),
            'format'    => 'JSON',
        ];

        //请求接口
        $result = $this->execute('YuanTongGetMailNo', $this->apiGetMailInfoUrl, $requestData);        
        if (!isset($result['success'])) {
            $resultjson= json_encode($result,JSON_UNESCAPED_UNICODE);
            $return= [
                'waybillNo'          => $result['mailNo'],//运单号
                'declareResult'      => true,//申报结果 false 失败  true 成功
                'message'            => "运单号申报成功",//结果信息
                'bigWord'            => $result['shortAddress'],//大字/三段码
                'packagePlace'       => $result['packageCenterName']??'',//集包地
                'originData'         => $resultjson,//返回的原始数据result字符串,
                'logisticCustomCode' => $this->config['logisticCustomCode'] ?? '',//物流公司海关编码
                'logisticCustomName' => $this->config['logisticCustomName'] ?? '',//物流公司海关名称
            ];
            return Normal::returnCode(Normal::SUC, "申报物流单号成功", $return);
        } else {
            $Message = $result['reason'] ?? '请求失败';
            $return  = [
                'DeclareResult' => false,
                'Message'       => "申请运单号失败【{$Message}】",
            ];
            return Normal::returnCode(Normal::FAIL, "获取物流单号失败【{$Message}】", $return);
        }
    }
    
    /**获得签名
     * @param $data
     * @return string
     */
    public function getSign($data) {
        $array = [$data['nonce'], $data['channelCode'], $data['clientKey'], $data['timestamp'], $data['data']];
        sort($array, SORT_STRING);
        $str        = implode($array);
        $relultData = [0, sha1($str)];
        return $relultData[1];
    }
    
    /**
     * 返回毫秒级时间戳
     * @return number
     */
    public static function getMillisecond() {
        list($s1, $s2) = explode(' ', microtime());
        return (string) sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000);
    }
    
     /**
     * 推送接口
     * @param string $method
     * @param array $body
     */
    public function execute(string $method ,string $url,array $body)
    {
        $parameter = [
            [
                'tag'       => 'yto_order_'.$method,
                'url'       => $url,
                'data'      => $body,
                'method'    => 'post',
                'format'    => 'json',
                'header'    => ['Content-type:application/json; charset=UTF-8']
            ],
        ];
        $resTask    = TaskWorker::instance()->addTask($parameter);
        $resTask    = current($resTask);
        $response   = @json_decode($resTask['response']['result'],true);        
        return $response;
    }
}
